﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using MouseExtender.Logic;
using MouseExtender.Logic.Common;
using MouseExtender.Logic.Entities;
using MouseExtender.Logic.Enums;
using MouseExtender.Logic.Events;
using MouseExtender.Logic.Helpers;
using MouseExtender.Logic.Managers;
using MouseExtender.Resources;

namespace MouseExtender.Controls.UserControls
{
    /// <summary>
    /// Interaction logic for LauncherUC.xaml
    /// </summary>
    public partial class LauncherUC : UserControl
    {
        #region fields

        private int _previousItemIndex = -1;
        private List<LauncherItemUC> _shiftedItems = new List<LauncherItemUC>();
        private object _shiftedItemsLock = new object();
        /// <summary>
        /// Cache with Items in order to decrease Tab loading and update time and cpu usage.
        /// </summary>
        private Dictionary<LauncherItem, LauncherItemUC> _launcherItemCache = new Dictionary<LauncherItem, LauncherItemUC>();
        private TabUC _activePanel = null;
        private TabUC _backPanel = null;
        private bool _switchIsActive = false;

        #endregion fields

        #region events

        public event LaunchItemSelected LaunchItemSelected;
        public event LaunchItemSelecting LaunchItemSelecting;

        #endregion events

        #region properties

        private Size ItemSize
        {
            get
            {
                return ActivePanel.ChildrenContainer[0].RenderSize;
            }
        }

        public bool DisableDragging
        {
            get;
            set;
        }

        /// <summary>
        /// Shows whether laucnitem dragging is in progress
        /// </summary>
        public bool DragIsActive
        {
            get;
            internal set;
        }

        public Canvas CanvasRoot
        {
            get { return cnvRoot; }
            set { cnvRoot = value; }
        }

        /// <summary>
        /// Active panel with (or without) items. Always initialized.
        /// </summary>
        public TabUC ActivePanel
        {
            get { return _activePanel; }
        }

        public Storyboard SwitchPanelsAnimation
        {
            get
            {
                return (Storyboard)this.Resources["sbMovePanel"];
            }
        }

        #endregion properties

        #region constructor

        public LauncherUC()
        {
            InitializeComponent();
            //init local panel variables
            _activePanel = tabPanelMain;
            _backPanel = tabPanelBack;
        }

        #endregion constructor

        #region event handlers

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            LoadLauncherItems(null);
        }

        private void newItem_ItemSelected(object sender, LaunchItemSelectedEventArgs e)
        {
            if (LaunchItemSelected != null)
                LaunchItemSelected(sender, e);
        }

        private void newItem_ItemSelecting(object sender, LaunchItemSelectingEventArgs e)
        {
            if (LaunchItemSelecting != null)
                LaunchItemSelecting(sender, e);
        }

        #endregion event handlers

        #region methods

        private void LoadLauncherItems(SwitchPanelInfo switchPanelsInfo)
        {
            MainWindow mainWindow = App.MainWindowInstance;
            TabInfo currentTabInfo = switchPanelsInfo == null ? GetTabInfoByTabIndex(mainWindow.ucTabs.CurrentTab) : switchPanelsInfo.CurrentTab;
            TabInfo prevTabInfo = switchPanelsInfo == null ? null : switchPanelsInfo.PreviousTab;

            //if current tab is already displayed no animation and no reloading is required
            //if (ActivePanel.TabInfo == null || ActivePanel.TabId != currentTabInfo.Id)
            if (prevTabInfo == null)
            {
                //TODO: create simple animation to show the first tab
                ActivePanel.Visibility = Visibility.Visible;
                ActivePanel.TabInfo = currentTabInfo;
            }
            else //need to load second tab and start swith animation
            {
                //if (_backPanel.TabInfo == null || _backPanel.TabId != currentTabInfo.Id)
                //{
                _backPanel.Visibility = Visibility.Visible;
                _backPanel.TabInfo = currentTabInfo;
                //}

                SwitchItemsPanels(switchPanelsInfo.SwitchToLeftDirection);
            }
        }

        private bool IsTabToShow(TabUC tabPanelToCheck, int tabId)
        {
            return tabPanelToCheck != _activePanel
                && tabPanelToCheck.TabInfo != null
                && tabPanelToCheck.TabId == tabId;
        }

        private TabInfo GetTabInfoByTabIndex(int currentTabId)
        {
            if (currentTabId == -1)
                return null;

            return SettingsManager.CurrentSettings.TabInfoList.Where(ti => ti.Id == currentTabId).Select(ti => ti).Single();
        }

        /// <summary>
        /// Gets existent or creates new LauncherItemUC for given LauncherItem with cache check.
        /// </summary>
        /// <param name="item">item metadata</param>
        /// <param name="isNeedSaveSeting">indicate whether it is required to save settings after method complete</param>
        /// <returns>incstance of <see cref="LauncherItemUC"/></returns>
        public LauncherItemUC GetLauncherItemUC(LauncherItem item, ref bool isNeedSaveSeting)
        {
            LauncherItemUC newItemUC = null;
            if (_launcherItemCache.ContainsKey(item))
            {
                newItemUC = _launcherItemCache[item];
            }
            else
            {
                newItemUC = new LauncherItemUC(item);
                newItemUC.DisableExtendedBehavior = DisableDragging;
                if (!DisableDragging)
                {
                    newItemUC.ItemDragging += new MouseEventHandler(newItem_ItemDragging);
                    newItemUC.ItemDropped += new MouseEventHandler(newItem_ItemDropped);
                }

                if (string.IsNullOrEmpty(item.ToolTip))
                {
                    InitializeToolTip(item);
                    isNeedSaveSeting = true;
                }
                newItemUC.ToolTipContent = item.ToolTip;

                try
                {
                    newItemUC.IsBroken = false;
                    BitmapSource source = IconHelper.GetItemIcon(item);
                    //TODO - freeze icons using source.Freeze();
                    newItemUC.imgIcon.Source = source;
                    if (item.ItemColor == Constants.Defaults.ItemColor || item.ItemColor == Constants.Defaults.BrokenItemColor)
                    {
                        if (!newItemUC.IsUrl)
                        {
                            newItemUC.ItemColor = IconHelper.GetIconMatchedColor(source, 9);
                        }
                        else
                        {
                            Random random = new Random(DateTime.Now.Millisecond);
                            Color randomColor = TabColors.Colors[random.Next(0, 9)];
                            HSBColor color = HSBColor.FromColor(System.Drawing.Color.FromArgb(randomColor.R, randomColor.G, randomColor.B));
                            HSBColor newColor = new HSBColor(color.H, random.Next(100, 150), random.Next(70, 85));
                            newItemUC.ItemColor = Color.FromRgb(newColor.Color.R, newColor.Color.G, newColor.Color.B);
                        }
                        isNeedSaveSeting = true;
                    }
                    newItemUC.ItemSelecting += new LaunchItemSelecting(newItem_ItemSelecting);
                    newItemUC.ItemSelected += new LaunchItemSelected(newItem_ItemSelected);
                }
                catch (Exception ex)
                {
                    Logger.HandleNonCriticalError(ex);
                    newItemUC.SetBrokenFileStyle();
                    newItemUC.toolTip.Content = ResourceManager.GetMessage(Constants.ResourceKeys.Launcher_FileNotExissMessageKey);
                    newItemUC.IsBroken = true;
                }

                switch (SettingsManager.CurrentSettings.IconSize)
                {
                    case IconSize.Icon16x16:
                        newItemUC.iconPlaceholder.Height = newItemUC.iconPlaceholder.Width =
                        newItemUC.itemButton.Height = newItemUC.itemButton.Width = 22;
                        newItemUC.iconPlaceholder.Clip = new RectangleGeometry(new Rect(-1, -1, 24, 24), 5, 5);
                        newItemUC.imgIcon.Width = newItemUC.imgIcon.Height = 16;
                        newItemUC.imgIcon.Margin = new Thickness(3);
                        newItemUC.glare.Width = 22;
                        newItemUC.glare.Height = 11;
                        Margin = new Thickness(0);
                        break;
                    case IconSize.Icon32x32:
                    default:
                        newItemUC.imgIcon.Width = newItemUC.imgIcon.Height = 32;
                        break;
                }

                List<TabInfo> contextMenuTabInfoList = new List<TabInfo>();
                foreach (TabInfo tabInfo in SettingsManager.CurrentSettings.TabInfoList)
                {
                    contextMenuTabInfoList.Add(new TabInfo()
                    {
                        Id = tabInfo.Id,
                        Name = tabInfo.Name,
                        ContextMenuOwner = newItemUC
                    }
                    );
                }
                newItemUC.sendTo.ItemsSource = contextMenuTabInfoList;

                //save to cache
                //little memory leak. launcher items clear only on drop cache upon settings change
                _launcherItemCache[item] = newItemUC;
            }

            return newItemUC;
        }

        /// <summary>
        /// Explicitly drops items cache
        /// </summary>
        internal void ClearItemsCache()
        {
            _launcherItemCache.Clear();
        }

        private void InitializeToolTip(LauncherItem item)
        {
            string toolTipText = string.Empty;

            if (Directory.Exists(item.LaunchPath))
            {
                DirectoryInfo info = new DirectoryInfo(item.LaunchPath);
                toolTipText = info.Name;
            }
            else
            {
                if (File.Exists(item.LaunchPath))
                {
                    FileVersionInfo info = FileVersionInfo.GetVersionInfo(item.LaunchPath);
                    toolTipText = info.FileDescription;
                    if (toolTipText == null)
                    {
                        toolTipText = Path.GetFileName(item.LaunchPath);
                    }
                }
            }
            item.ToolTip = toolTipText;
        }

        public void AddFile()
        {
            MainWindow mainWindow = App.MainWindowInstance;
            System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
            ofd.Title = UIResources.OpenFileDialog_Title;
            ofd.Filter = Constants.FileFilters.AllFiles;
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                UserSettings settings = SettingsManager.CurrentSettings;
                string relativeFileName = PathHelper.RelativeToAppBaseFolderPath(ofd.FileName);
                string relativeWorkingDirName = PathHelper.RelativeToAppBaseFolderPath(Path.GetDirectoryName(ofd.FileName));

                LauncherItem item = new LauncherItem()
                {
                    LaunchPath = relativeFileName,
                    ItemNumber = settings.LauncherItems.Count,
                    WorkingDirectory = relativeWorkingDirName,
                    TabId = mainWindow.ucTabs.CurrentTab,
                    IsUrl = false
                };
                string toolTipText = FileVersionInfo.GetVersionInfo(ofd.FileName).FileDescription;
                if (string.IsNullOrEmpty(toolTipText))
                    toolTipText = ofd.SafeFileName;
                item.ToolTip = toolTipText;
                settings.LauncherItems.Add(item);
                settings.SettingChanged();
            }
            Refresh();
            mainWindow.ShowWindow();
        }

        public void AddFile(string filename)
        {
            AddFile(filename, null, -1, null, null);
        }

        public void AddFile(string filename, string iconLocation, int iconIndex, string workingDirectory, string arguments)
        {
            MainWindow mainWindow = App.MainWindowInstance;
            UserSettings settings = SettingsManager.CurrentSettings;

            string iconLocationForItem = null;
            int iconIndexForItem = -1;
            if (iconLocation != null)
            {
                iconIndexForItem = iconIndex;
                iconLocationForItem = iconLocation;
            }
            string workingDirectoryForItem = String.IsNullOrEmpty(workingDirectory) ? Path.GetDirectoryName(filename) : workingDirectory;

            string relativeFileName = PathHelper.RelativeToAppBaseFolderPath(filename);
            string relativeWorkingDirName = PathHelper.RelativeToAppBaseFolderPath(workingDirectoryForItem);

            LauncherItem item = new LauncherItem()
            {
                LaunchPath = relativeFileName,
                ItemNumber = settings.LauncherItems.Count,
                TabId = mainWindow.ucTabs.CurrentTab,
                IsUrl = false,
                IconSource = iconLocationForItem,
                IconIndex = iconIndexForItem,
                WorkingDirectory = relativeWorkingDirName,
                Arguments = arguments,
            };

            string toolTipText = FileVersionInfo.GetVersionInfo(filename).FileDescription;
            if (!string.IsNullOrEmpty(toolTipText))
            {
                item.ToolTip = toolTipText;
            }
            settings.LauncherItems.Add(item);
            settings.SettingChanged();

            Refresh();
            mainWindow.ShowWindow();
        }

        //TODO: refactor - create 1 method with overloading (no logic duplication)
        public void AddFolder()
        {
            MainWindow mainWindow = App.MainWindowInstance;
            System.Windows.Forms.FolderBrowserDialog fbd = new System.Windows.Forms.FolderBrowserDialog();
            if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                AddFolderInternal(fbd.SelectedPath);
            }
            mainWindow.ShowWindow();
        }

        public void AddFolder(string foldername)
        {
            MainWindow mainWindow = App.MainWindowInstance;
            AddFolderInternal(foldername);
            mainWindow.ShowWindow();
        }

        private void AddFolderInternal(string foldername)
        {
            MainWindow mainWindow = App.MainWindowInstance;
            UserSettings settings = SettingsManager.CurrentSettings;
            DirectoryInfo folderInfo = new DirectoryInfo(foldername);
            LauncherItem item = new LauncherItem()
            {
                LaunchPath = foldername,
                ItemNumber = settings.LauncherItems.Count,
                TabId = mainWindow.ucTabs.CurrentTab,
                IsUrl = false,
                ToolTip = folderInfo.Name
            };
            settings.LauncherItems.Add(item);
            settings.SettingChanged();
            Refresh();
        }

        public void DeleteItem(int itemNumber)
        {
            MainWindow mainWindow = App.MainWindowInstance;
            UserSettings settings = SettingsManager.CurrentSettings;
            LauncherItem itemToRemove = settings.LauncherItems.Where(i => i.ItemNumber == itemNumber).SingleOrDefault();
            if (itemToRemove == null)
                return;
            settings.LauncherItems.Remove(itemToRemove);
            mainWindow.NormalizeLauncherItemNumbers();
            settings.SettingChanged();
            //if need to remove thumbs then remove and save
            if (!string.IsNullOrEmpty(itemToRemove.ThumbsImageId))
            {
                ExternalResourcesManager.Thumbs[itemToRemove.ThumbsImageId] = null;
                ExternalResourcesManager.SaveThumbs();
            }
            Refresh();
            mainWindow.ShowWindow();
        }

        public void Refresh()
        {
            Refresh(null);
        }

        public void Refresh(SwitchPanelInfo switchPanelInfo)
        {
            Refresh(false, switchPanelInfo);
        }

        public void Refresh(bool dropItemCache, SwitchPanelInfo switchPanelInfo)
        {
            if (_switchIsActive)
            {
                FinishSwitch();
            }

            if (_switchIsActive)
            {
                throw new InvalidOperationException("Animtaion should't be started");
            }

            if (dropItemCache)
                ClearItemsCache();

            LoadLauncherItems(switchPanelInfo);
            App.MainWindowInstance.SetEnableDisableMenuItem();
        }

        #endregion

        #region switch panel methods

        private void SwitchItemsPanels(bool switchToLeft)
        {
            //TODO:enable spline animation

            _switchIsActive = true;

            //double newLeftPos = 0;
            //in order to use ActualWidth element should be visible
            //_backPanel.Visibility = Visibility.Visible;

            ////Trace.WriteLine(string.Format("Shift panels. act.left/act.width/bck.left/bck.width={0}/{1}/{2}/{3}", _activePanel.LeftPosition, _activePanel.ActualWidth, _backPanel.LeftPosition, _backPanel.ActualWidth));

            //double activePanelOriginLeft = 0; //_activePanel.LeftPosition

            //if (switchToLeft)
            //{
            //    newLeftPos = activePanelOriginLeft + _activePanel.ActualWidth;
            //}
            //else
            //{
            //    newLeftPos = activePanelOriginLeft - _backPanel.ActualWidth;
            //}

            ////set initial position.. with binding+convertor possible is not required
            //_backPanel.LeftPosition = newLeftPos;

            ////set binding
            //Binding leftBinding = new Binding("(Canvas.Left)");
            //leftBinding.Source = _backPanel;
            //leftBinding.Mode = BindingMode.OneWay;
            //leftBinding.Converter = new DoubleWithShiftConverter();
            //leftBinding.ConverterParameter = newLeftPos;
            //_activePanel.SetBinding(Canvas.LeftProperty, leftBinding);

            //start animation
            Storyboard movePanel = SwitchPanelsAnimation;

            DoubleAnimationUsingKeyFrames changeMainPanelLeft = (DoubleAnimationUsingKeyFrames)movePanel.Children[0];
            changeMainPanelLeft.SetValue(Storyboard.TargetProperty, _activePanel);
            changeMainPanelLeft.KeyFrames[0].Value = 0;
            changeMainPanelLeft.KeyFrames[1].Value = switchToLeft ? 0 - _activePanel.ActualWidth : _activePanel.ActualWidth;

            DoubleAnimationUsingKeyFrames changeBackPanelLeft = (DoubleAnimationUsingKeyFrames)movePanel.Children[1];
            changeBackPanelLeft.SetValue(Storyboard.TargetProperty, _backPanel);
            changeBackPanelLeft.KeyFrames[0].Value = switchToLeft ? _activePanel.ActualWidth : 0 - _activePanel.ActualWidth;
            changeBackPanelLeft.KeyFrames[1].Value = 0;

            DoubleAnimationUsingKeyFrames changeCanvasHeight = (DoubleAnimationUsingKeyFrames)movePanel.Children[2];
            changeCanvasHeight.KeyFrames[0].Value = _activePanel.ActualHeight;
            changeCanvasHeight.KeyFrames[1].Value = _backPanel.ActualHeight;

            //DoubleAnimation changeMainPanelLeft = (DoubleAnimation)movePanel.Children[0];
            //changeMainPanelLeft.SetValue(Storyboard.TargetProperty, _activePanel);
            //changeMainPanelLeft.From = 0;
            //changeMainPanelLeft.To = switchToLeft ? 0 - _activePanel.ActualWidth : _activePanel.ActualWidth;

            //DoubleAnimation changeBackPanelLeft = (DoubleAnimation)movePanel.Children[1];
            //changeBackPanelLeft.SetValue(Storyboard.TargetProperty, _backPanel);
            //changeBackPanelLeft.From = switchToLeft ? _activePanel.ActualWidth : 0 - _activePanel.ActualWidth;
            //changeBackPanelLeft.To = 0;

            //DoubleAnimation changeCanvasHeight = (DoubleAnimation)movePanel.Children[2];
            //changeCanvasHeight.From = _activePanel.ActualHeight;
            //changeCanvasHeight.To = _backPanel.ActualHeight;

            //start animation
            movePanel.Begin();
        }

        private void FinishSwitch()
        {
            if (!_switchIsActive)
            {
                return;
            }

            Storyboard movePanel = SwitchPanelsAnimation;
            movePanel.Stop();

            _backPanel.LeftPosition = 0;

            //BindingOperations.ClearBinding(_activePanel, Canvas.LeftProperty);
            _activePanel.Visibility = System.Windows.Visibility.Collapsed;
            _activePanel.ChildrenContainer.Clear(); //clear tab - allows items to be added at another tab

            _activePanel = _backPanel;
            _backPanel = _activePanel == tabPanelMain ? tabPanelBack : tabPanelMain;

            UpdateCanvasRootSize();

            _switchIsActive = false;

            //Trace.WriteLine(string.Format("Finishing switch. Active Left = [{0}], Back left = [{1}]", _activePanel.LeftPosition, _backPanel.LeftPosition));
        }

        private void sbMovePanel_Completed(object sender, EventArgs e)
        {
            FinishSwitch();
        }

        #endregion switch panel methods

        #region Drag'n'Drop Logic

        private void newItem_ItemDragging(object sender, MouseEventArgs e)
        {
            int originalItemIndex;
            int expectedItemIndex;

            DragIsActive = true;
            LauncherItemUC itemControl = GetOriginalAndNewIndicies(sender, e, out originalItemIndex, out expectedItemIndex);

            if (_previousItemIndex != expectedItemIndex)
            {
                bool movingUp = expectedItemIndex > originalItemIndex;
                List<LauncherItemUC> affectedItems = GetAffectedItems(originalItemIndex, expectedItemIndex, true);

                foreach (LauncherItemUC affectedItem in affectedItems)
                {
                    int originalIndex = GetOriginalItemIndex(affectedItem);
                    int expectedIndex = originalIndex + (movingUp ? -1 : +1);
                    Vector shift = GetShiftToExpectedPlace(originalIndex, expectedIndex);
                    affectedItem.ShiftBy(shift);
                }

                lock (_shiftedItemsLock)
                {
                    List<LauncherItemUC> itemsToRestore = _shiftedItems.Except(affectedItems).ToList();
                    itemsToRestore.ForEach(item => item.RestorePosition());
                    _shiftedItems = affectedItems;
                }
            }

            _previousItemIndex = expectedItemIndex;
        }

        private void newItem_ItemDropped(object sender, MouseEventArgs e)
        {
            int originalItemIndex;
            int expectedItemIndex;

            LauncherItemUC itemControl = GetOriginalAndNewIndicies(sender, e, out originalItemIndex, out expectedItemIndex);

            //if replacement needed
            bool replacementNeeded = originalItemIndex != expectedItemIndex;
            if (replacementNeeded)
            {
                Vector shift = GetShiftToExpectedPlace(originalItemIndex, expectedItemIndex);
                itemControl.ShiftBy(shift, ItemControlShiftedCallback, e);
            }
            else
            {
                itemControl.RestorePosition();
            }

            lock (_shiftedItemsLock)
            {
                _shiftedItems.RemoveAll(item => true);
                _previousItemIndex = -1;
            }

            if (!replacementNeeded)
                DragIsActive = false;
        }

        private void ItemControlShiftedCallback(object sender, MouseEventArgs e)
        {
            //get dropped item original and new indicies
            int oldItemIndex;
            int newItemIndex;
            LauncherItemUC itemControl = GetOriginalAndNewIndicies(sender, e, out oldItemIndex, out newItemIndex);

            List<LauncherItemUC> affectedItems = GetAffectedItems(oldItemIndex, newItemIndex, false);

            //replace items
            ActivePanel.ChildrenContainer.Remove(itemControl);
            ActivePanel.ChildrenContainer.Insert(newItemIndex, itemControl);

            //Set Default position
            foreach (LauncherItemUC affectedItem in affectedItems)
            {
                affectedItem.MoveTransform.X = 0;
                affectedItem.MoveTransform.Y = 0;
            }

            //change settings
            int delta = newItemIndex > oldItemIndex ? -1 : 1;
            int newItemNumber = ((LauncherItemUC)ActivePanel.ChildrenContainer[newItemIndex]).ItemNumber;
            for (int index = newItemIndex; (delta < 0) ? index >= oldItemIndex : index <= oldItemIndex; index += delta)
            {
                if (index == oldItemIndex)
                {
                    ((LauncherItemUC)ActivePanel.ChildrenContainer[index]).Item.ItemNumber = newItemNumber;
                }
                else
                {
                    ((LauncherItemUC)ActivePanel.ChildrenContainer[index]).Item.ItemNumber = ((LauncherItemUC)ActivePanel.ChildrenContainer[index + delta]).ItemNumber;
                }
            }
            App.MainWindowInstance.NormalizeLauncherItemNumbers();
            SettingsManager.CurrentSettings.SettingChanged();

            DragIsActive = false;
        }

        private LauncherItemUC GetOriginalAndNewIndicies(object sender, MouseEventArgs e, out int originalItemIndex, out int expectedItemIndex)
        {
            LauncherItemUC itemControl = (LauncherItemUC)sender;
            Point itemPosition = GetCurrentItemPosition(e, itemControl);

            originalItemIndex = GetOriginalItemIndex(itemControl);
            expectedItemIndex = GetExpectedItemIndex(itemPosition);
            return itemControl;
        }

        private int GetOriginalItemIndex(LauncherItemUC itemControl)
        {
            int originalItemIndex = ActivePanel.ChildrenContainer.IndexOf(itemControl);
            return originalItemIndex;
        }

        private List<LauncherItemUC> GetAffectedItems(int originalIndex, int expectedIndex, bool excludeOriginalItem)
        {
            int start = Math.Min(originalIndex, expectedIndex);
            int finish = Math.Max(originalIndex, expectedIndex);
            //exclede the target item
            if (excludeOriginalItem)
            {
                if (originalIndex < expectedIndex)
                    start++;
                else
                    finish--;
            }
            List<LauncherItemUC> changedItems = ActivePanel.ChildrenContainer.OfType<LauncherItemUC>().Skip(start).Take(finish - start + 1).ToList();

            return changedItems;
        }

        private Vector GetShiftToExpectedPlace(int originalIndex, int expectedIndex)
        {
            Point itemPosition = GetItemOriginalPosition(originalIndex);
            Point expectedPosition = GetItemOriginalPosition(expectedIndex);
            Vector shift = expectedPosition - itemPosition;
            return shift;
        }

        private Point GetCurrentItemPosition(MouseEventArgs e, LauncherItemUC itemControl)
        {
            Point currentPosition = e.GetPosition(ActivePanel);
            Point itemMousePosition = e.GetPosition(itemControl);
            Point itemPosition = new Point(currentPosition.X - itemMousePosition.X, currentPosition.Y - itemMousePosition.Y);
            return itemPosition;
        }

        private Point GetItemOriginalPosition(int itemIndex)
        {
            Size itemSize = ItemSize;
            int itemsInRow = (int)(ActivePanel.ActualWidth / itemSize.Width);
            int itemRow = itemIndex / itemsInRow;
            int itemColumn = itemIndex - itemRow * itemsInRow; ;
            Point itemPosition = new Point(itemColumn * itemSize.Width, itemRow * itemSize.Height);
            return itemPosition;
        }

        private int GetExpectedItemIndex(Point itemPosition)
        {
            Size itemSize = ItemSize;
            int columnsCount = (int)(ActivePanel.ActualWidth / itemSize.Width);
            int rowsCount = (int)(ActivePanel.ActualHeight / itemSize.Height);

            int itemRow = (int)(itemPosition.Y / itemSize.Height + 0.5);
            int itemColumn = (int)(itemPosition.X / itemSize.Width + 0.5);

            if (itemRow >= rowsCount)
                itemRow = rowsCount - 1;
            if (itemRow < 0)
                itemRow = 0;
            if (itemColumn >= columnsCount)
                itemColumn = columnsCount - 1;
            if (itemColumn < 0)
                itemColumn = 0;

            int position = columnsCount * itemRow + itemColumn;
            if (position >= ActivePanel.ChildrenContainer.Count)
                position = ActivePanel.ChildrenContainer.Count - 1;
            return position;
        }

        #endregion

        private void tabPanel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //TODO: move to bindings
            if (sender == this.ActivePanel)
            {
                UpdateCanvasRootSize();
            }
        }

        private void UpdateCanvasRootSize()
        {
            cnvRoot.Height = ActivePanel.ActualHeight;
            cnvRoot.Width = ActivePanel.ActualWidth;
        }
    }
}
